
package com.studiofortress.sf.util;

/**
 * A Thread which continually calls it's abstract repeat method, repeatedly. It
 * will do this constantly until stopRepeat is called. stopRepeat will not stop
 * the Thread, it simply stops calling repeat and will allow the thread to end
 * naturally (i.e. it will run out of code).
 *
 * @author Joseph Lenton
 */
public abstract class Repeater extends Thread
{
    /**
     * This is the value that states that this Thread will not sleep between
     * calls to the repeat method.
     */
    private static final long NO_SLEEP = 0;

    private boolean isRepeating;
    private final long sleepTime;

    /**
     * Creates a Repeater which does not sleep between calls to repeat.
     */
    public Repeater()
    {
        this(NO_SLEEP);
    }

    /**
     * Creates a new Repeater which will sleep for the given amount of time
     * between calls to repeat. If sleepTime is set to 0 then it will simply not
     * sleep between calls to repeat.
     * @param sleepTime The amount of time to sleep, in milliseconds, between calls to repeat. Cannot be less then 0.
     */
    public Repeater(final long sleepTime)
    {
        if (sleepTime < 0) {
            throw new IllegalArgumentException("The given sleepTime cannot be less then 0, given: " + sleepTime);
        }
        this.sleepTime = sleepTime;
        isRepeating = true;
    }

    /**
     * @return The amount of time to sleep between calls to repeat.
     */
    public long getSleepTime()
    {
        return sleepTime;
    }

    /**
     * Starts this Thread to run.
     */
    @Override
    public synchronized void start()
    {
        isRepeating = true;
        super.start();
    }

    /**
     * Calls the repeat method repeatedly and sleeps for the amount of sleepTime
     * stored.
     */
    @Override
    public void run()
    {
        while (isRepeating()) {
            repeat();

            final long sleepTime = getSleepTime();
            if (sleepTime > NO_SLEEP) {
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) { }
            }
            yield();
        }
    }

    /**
     * This is the method called repeatedly by this Thread.
     */
    public abstract void repeat();

    /**
     * @return True if it will continue to call the repeat method, otherwise false if it will not.
     */
    public boolean isRepeating()
    {
        return isRepeating;
    }

    /**
     * From now on isRepeating will return false and it will stop calling the
     * repeat method.
     */
    public void stopRepeating()
    {
        isRepeating = false;
    }
}
